package org.summer.lawyer.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.summer.common.core.exception.ServiceException;
import org.summer.common.core.utils.MapstructUtils;
import org.summer.common.json.utils.JsonUtils;
import org.summer.common.mybatis.core.page.PageQuery;
import org.summer.common.mybatis.core.page.TableDataInfo;
import org.summer.common.satoken.utils.LoginHelper;
import org.summer.lawyer.constant.SystemConstants;
import org.summer.lawyer.constant.enums.PaymentMethod;
import org.summer.lawyer.constant.enums.WithdrawStatus;
import org.summer.lawyer.domain.UserWithdraw;
import org.summer.lawyer.domain.bo.userwallet.UserWalletBo;
import org.summer.lawyer.domain.bo.userwithdraw.UserWithdrawBo;
import org.summer.lawyer.domain.bo.userwithdraw.WithdrawQrcodeBo;
import org.summer.lawyer.domain.vo.brokerageconfig.BrokerageConfigVo;
import org.summer.lawyer.domain.vo.paychannel.PayChannelVo;
import org.summer.lawyer.domain.vo.userwallet.UserWalletVo;
import org.summer.lawyer.domain.vo.userwithdraw.UserWithdrawVo;
import org.summer.lawyer.domain.vo.userwithdraw.WithdrawQrcodeVo;
import org.summer.lawyer.domain.vo.wxuser.WxUserVo;
import org.summer.lawyer.mapper.UserWithdrawMapper;
import org.summer.lawyer.service.*;
import org.summer.lawyer.util.IdWorkerUtil;
import org.summer.system.domain.bo.SysDictDataBo;
import org.summer.system.domain.vo.SysDictDataVo;
import org.summer.system.service.ISysDictDataService;

import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 用户提现记录Service业务层处理
 *
 * @author summer
 * @date 2024-06-25
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class UserWithdrawServiceImpl extends ServiceImpl<UserWithdrawMapper, UserWithdraw> implements IUserWithdrawService {

    private final IWxUserService wxUserService;
    private final IUserWalletService userWalletService;
    private final IPayChannelService payChannelService;
    private final IBrokerageConfigService brokerageConfigService;
    private final ISysDictDataService sysDictDataService;

    /**
     * 查询用户提现记录
     *
     * @param id 主键
     * @return 用户提现记录
     */
    @Override
    public UserWithdrawVo selectUserWithdrawVoById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public UserWithdrawVo selectUserWithdrawVoByWithdrawNumber(String withdrawNumber) {
        LambdaQueryWrapper<UserWithdraw> lqw = Wrappers.lambdaQuery();
        lqw.eq(UserWithdraw::getWithdrawNumber, withdrawNumber);
        return baseMapper.selectVoOne(lqw, false);
    }

    /**
     * 分页查询用户提现记录列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 用户提现记录分页列表
     */
    @Override
    public TableDataInfo<UserWithdrawVo> selectPageList(UserWithdrawBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<UserWithdraw> lqw = buildQueryWrapper(bo);
        lqw.orderByDesc(UserWithdraw::getId);
        Page<UserWithdrawVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的用户提现记录列表
     *
     * @param bo 查询条件
     * @return 用户提现记录列表
     */
    @Override
    public List<UserWithdrawVo> selectUserWithdrawVoList(UserWithdrawBo bo) {
        LambdaQueryWrapper<UserWithdraw> lqw = Wrappers.lambdaQuery();
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<UserWithdraw> buildQueryWrapper(UserWithdrawBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<UserWithdraw> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getAppId() != null, UserWithdraw::getAppId, bo.getAppId());
        lqw.eq(StrUtil.isNotBlank(bo.getMchNo()), UserWithdraw::getMchNo, bo.getMchNo());
        lqw.like(StrUtil.isNotBlank(bo.getMchName()), UserWithdraw::getMchName, bo.getMchName());
        lqw.eq(StrUtil.isNotBlank(bo.getTradeNo()), UserWithdraw::getTradeNo, bo.getTradeNo());
        lqw.eq(bo.getPayOrderId() != null, UserWithdraw::getPayOrderId, bo.getPayOrderId());
        lqw.eq(bo.getWxUserId() != null, UserWithdraw::getWxUserId, bo.getWxUserId());
        lqw.eq(StrUtil.isNotBlank(bo.getWithdrawWay()), UserWithdraw::getWithdrawWay, bo.getWithdrawWay());
        lqw.eq(StrUtil.isNotBlank(bo.getWithdrawStatus()), UserWithdraw::getWithdrawStatus, bo.getWithdrawStatus());
        lqw.eq(StrUtil.isNotBlank(bo.getWithdrawNumber()), UserWithdraw::getWithdrawNumber, bo.getWithdrawNumber());
        lqw.eq(StrUtil.isNotBlank(bo.getReceivableAccount()), UserWithdraw::getReceivableAccount, bo.getReceivableAccount());
        lqw.like(StrUtil.isNotBlank(bo.getRealName()), UserWithdraw::getRealName, bo.getRealName());
        lqw.eq(StrUtil.isNotBlank(bo.getOpeningBank()), UserWithdraw::getOpeningBank, bo.getOpeningBank());
        lqw.eq(bo.getAmount() != null, UserWithdraw::getAmount, bo.getAmount());
        lqw.eq(StrUtil.isNotBlank(bo.getReviewReason()), UserWithdraw::getReviewReason, bo.getReviewReason());
        lqw.eq(bo.getReviewerId() != null, UserWithdraw::getReviewerId, bo.getReviewerId());
        lqw.like(StrUtil.isNotBlank(bo.getReviewerName()), UserWithdraw::getReviewerName, bo.getReviewerName());
        lqw.eq(bo.getReviewTime() != null, UserWithdraw::getReviewTime, bo.getReviewTime());
        return lqw;
    }

    /**
     * 新增用户提现记录
     *
     * @param bo 用户提现记录
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(UserWithdrawBo bo) {
        UserWithdraw add = MapstructUtils.convert(bo, UserWithdraw.class);
        validEntityBeforeSave(add);
        //查询支付通道信息
        PayChannelVo channelVo = payChannelService.selectPayChannelVoByCode(PaymentMethod.WECHAT_MINIAPP.getCode(), bo.getAppId());
        if (channelVo == null) {
            throw new ServiceException("支付通道已关闭，请稍后再试");
        }
        add.setMchNo(channelVo.getMchNo());
        add.setMchName(channelVo.getMchName());
        add.setWithdrawStatus(WithdrawStatus.PENDING.getCode());
        add.setWithdrawNumber(IdWorkerUtil.nextIdStr());
        add.setCreateTime(DateUtil.date());
        add.setUpdateTime(DateUtil.date());
        if (LoginHelper.getLoginUser() != null) {
            add.setCreateBy(LoginHelper.getUserId());
            add.setUpdateBy(LoginHelper.getUserId());
        }
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    @Lock4j(keys = "{#bo.wxUserId}", acquireTimeout = 1000, expire = 10000)
    @DSTransactional(rollbackFor = Exception.class)
    @Override
    public void applyWithdraw(UserWithdrawBo bo) {
        WxUserVo wxUser = wxUserService.selectWxUserVoById(bo.getWxUserId());
        if (wxUser == null) {
            throw new ServiceException("用户信息未找到");
        }
        bo.setRealName(wxUser.getNickname());
        bo.setPhoneNumber(wxUser.getPhoneNumber());
        bo.setAvatar(wxUser.getAvatar());
        //查询钱包并减去余额
        UserWalletVo walletVo = userWalletService.selectUserWalletVoByUserId(bo.getWxUserId());
        log.info("[applyWithdraw][用户：{}]钱包信息：{}", bo.getWxUserId(), JsonUtils.toJsonString(walletVo));
        if (walletVo.getBalanceFee() < bo.getAmount()) {
            throw new ServiceException("余额不足");
        }
        if (walletVo.getWithdrawMinPrice() > bo.getAmount()) {
            throw new ServiceException(StrUtil.format("最低提现金额为 {} 元", walletVo.getWithdrawMinPrice() / 100));
        }
        //查询提现手续费
        BrokerageConfigVo brokerageConfigVo = brokerageConfigService.selectOneBrokerageConfigVo();
        bo.setWithdrawFeePercent(brokerageConfigVo.getWithdrawFeePercent());
        //计算实际到账金额
        Double payment = bo.getAmount() * brokerageConfigVo.getWithdrawFeePercent() * 0.01;
        log.info("[applyWithdraw][用户：{}]提现金额 {} 分，实际到账金额 {} 分， 收费百分比：{}", bo.getWxUserId(), bo.getAmount(), payment.longValue(), brokerageConfigVo.getWithdrawFeePercent());
        bo.setPayment(payment.longValue());
        Long balanceFee = walletVo.getBalanceFee() - bo.getAmount();
        // Long frozenAmount = walletVo.getFrozenAmount() + bo.getAmount();
        UserWalletBo walletBo = new UserWalletBo();
        BeanUtils.copyProperties(walletVo, walletBo);
        walletBo.setBalanceFee(balanceFee);
        //walletBo.setFrozenAmount(frozenAmount);
        userWalletService.updateByBo(walletBo);
        //保存提现记录
        this.insertByBo(bo);
    }

    /**
     * 修改用户提现记录
     *
     * @param bo 用户提现记录
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(UserWithdrawBo bo) {
        UserWithdraw update = MapstructUtils.convert(bo, UserWithdraw.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(UserWithdraw entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除用户提现记录信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 通过提现并发起转账
     */
    @Override
    public void confirmWithdraw(UserWithdrawBo bo) {

    }

    /**
     * 驳回提现申请
     */
    @Lock4j(keys = "{#bo.id}", acquireTimeout = 1000, expire = 10000)
    @DSTransactional(rollbackFor = Exception.class)
    @Override
    public void rejectWithdraw(UserWithdrawBo bo) {
        UserWithdrawVo vo = selectUserWithdrawVoById(bo.getId());
        if (vo == null) {
            throw new ServiceException("提现记录未找到");
        }
        //获取用户钱包记录
        UserWalletVo walletVo = userWalletService.selectUserWalletVoByUserId(vo.getWxUserId());
        if (walletVo.getFrozenAmount() < vo.getAmount()) {
            throw new ServiceException("用户冻结金额异常，小于提现金额");
        }
        Long frozenAmount = walletVo.getFrozenAmount() - vo.getAmount();
        Long balanceFee = walletVo.getBalanceFee() + vo.getAmount();
        UserWalletBo walletBo = new UserWalletBo();
        BeanUtils.copyProperties(walletVo, walletBo);
        walletBo.setBalanceFee(balanceFee);
        walletBo.setFrozenAmount(frozenAmount);
        userWalletService.updateByBo(walletBo);
        //更新提现记录
        UserWithdraw update = new UserWithdraw();
        update.setId(bo.getId());
        update.setUpdateTime(DateUtil.date());
        update.setReviewReason(bo.getReviewReason());
        update.setReviewTime(DateUtil.date());
        update.setWithdrawStatus(WithdrawStatus.REJECTION.getCode());
        if (LoginHelper.getLoginUser() != null) {
            update.setUpdateBy(LoginHelper.getUserId());
            update.setReviewerId(LoginHelper.getUserId());
            update.setReviewerName(LoginHelper.getLoginUser().getNickname());
        }
        LambdaUpdateWrapper<UserWithdraw> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(UserWithdraw::getId, bo.getId());
        baseMapper.update(update, updateWrapper);
    }

    @Override
    public void updateWithdrawQrCodeImage(WithdrawQrcodeBo bo) {
        SysDictDataVo vo = sysDictDataService.selectDictDataById(bo.getQrcodeId());
        SysDictDataBo dbo = new SysDictDataBo();
        if(ObjectUtil.isNull(vo)){
            dbo.setDictType(SystemConstants.WITHDRAW_QRCODE_TYPE_KEY);
            dbo.setDictLabel(SystemConstants.WITHDRAW_QRCODE_IMAGE_LABEL);
             List<SysDictDataVo> vos = sysDictDataService.selectDictDataList(dbo);
             if(CollUtil.isNotEmpty(vos)){
                 vo = vos.get(0);
             }else{
                 dbo.setDictSort(1);
                 dbo.setDictValue(bo.getQrcodeUrl());
                 sysDictDataService.insertDictData(dbo);
             }
        }else{
            BeanUtil.copyProperties(vo,dbo);
            dbo.setDictValue(bo.getQrcodeUrl());
            sysDictDataService.updateDictData(dbo);
        }
    }

    @Override
    public WithdrawQrcodeVo selectWithdrawQrcodeVoById(WithdrawQrcodeBo bo) {
        SysDictDataBo dbo = new SysDictDataBo();
        dbo.setDictType(SystemConstants.WITHDRAW_QRCODE_TYPE_KEY);
        dbo.setDictLabel(SystemConstants.WITHDRAW_QRCODE_IMAGE_LABEL);
        List<SysDictDataVo> vos = sysDictDataService.selectDictDataList(dbo);
        if(ObjectUtil.isEmpty(vos)){
            return null;
        }
        SysDictDataVo dictDataVo = vos.get(0);
        WithdrawQrcodeVo vo2 = new WithdrawQrcodeVo();
        vo2.setQrcodeId(dictDataVo.getDictCode());
        vo2.setQrcodeUrl(dictDataVo.getDictValue());
        return vo2;
    }
}
